<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
    </div>
</body>

<script>


</script>



<script>
    const Vue = {
        createRenderer({ querySelector, insert, createElement }){
            return{
                createApp(options){
                    return{
                        mount(selector){
                            const parent = querySelector(selector)

                            if(!options.render){
                                options.render = this.compile(parent.innerHTML)
                            }

                            if(options.setup){
                                this.setupState = options.setup()
                            }

                            if(options.data){
                                this.data = options.data()
                            }

                            const proxy =  new Proxy(this,{
                                get(target,key){
                                    if(target.setupState && key in target.setupState){
                                        return target.setupState[key]
                                    }else{
                                        return target.data[key]
                                    }
                                },
                                set(target,key,val){
                                    if(target.setupState && key in target.setupState){
                                        target.setupState[key] = val
                                    }else{
                                        target.data[key] = val
                                    }
                                }
                            })
                            
                            this.update = effect(()=>{
                                const vnode = options.render.call(proxy)

                                if(!this.isMounted){
                                    const el = this.createElm(vnode)
                                    parent.innerHTML = ''
                                    insert(el,parent)
                                    
                                    // this.isMounted = true
                                }else{

                                }
                                

                            })

                            // this.update()

                        },

                        createElm({tag, props,children}){
                            const el = createElement(tag)
                            if(typeof children === 'string'){
                                el.textContent = children
                            }else{
                                children.forEach(child=>{
                                    insert(this.createElm(child),el)
                                })
                            }
                            return el;
                        },
                        compile(template){
                            return function render(){
                                // const h3 = document.createElement('h3')
                                // h3.textContent = this.title
                                // return h3

                                return h('h3',null,[
                                    h('p',null,this.title),
                                    h('p',null,this.title),
                                    h('p',null,this.title),
                                    h('p',null,this.title),
                                ])
                            }
                        }
                    }
                }
            }
        },


        //平台通用转化
        createApp(options) {
            const renderer = Vue.createRenderer({
                querySelector(sel){
                    return document.querySelector(sel)
                },
                insert(el,parent){
                    parent.appendChild(el)
                },
                createElement(tag){
                    return document.createElement(tag)
                }
            });
            return renderer.createApp(options)
        },
    };

</script>

<script>
    //传入西悉尼，返回vnode，从而进行虚拟dom构建
function h(tag, props, children){
    return {tag, props, children}
}


function reactive(obj){
    return new Proxy(obj,{
        get(target,key){
            console.log('get-key',key,target[key]);

            track(target,key)
            return target[key];
        },
        set(target,key,val){
            console.log('set-key',key,val);
            target[key] = val;
//这个触发条件是写死的，还有优化的空间
            // app.update();

            trigger(target,key)
        }
    });
}

const effectStack = []
function effect(fn){
    const eff = function(){
        try{
            effectStack.push(eff)
            fn()
        }finally{
            effectStack.pop()
        }
    }

    eff()

    return eff
}



const targetMap = {}

function track(target,key){
    const effect = effectStack[effectStack.length-1]
    let map = targetMap[target]
    if (!map){
        // 首次获得
        map = targetMap[target] = {}
    }

    let deps = map[key]
    if(!deps){
        deps = map[key] = new Set()
    }
    
// 如果该副函数不在其中则添加进去
    deps.add(effect)

    // if(deps.indexOf(effect) === -1){
    //     deps.push(effect)
    // }
}


function trigger(target,key){
    const map = targetMap[target]
    if (map){
        const deps = map[key]
    
        if(deps){
            deps.forEach(dep=>dep())
        }
    }
}

// const obj = reactive({foo:'foo'})
// effect(()=>{
//     console.log(obj.foo);
// })
// obj.foo = 'foo~~~~~~~~'





    const app = Vue.createApp({
        data(){
            return{
                title:'hello,vue3'
            }
        },
        setup(){
            const state = reactive({
                title:'vue3,hello'
            })
            setTimeout(() => {
                state.title = 'hello,vue3!'
            },2000);
            return state
        }
    })

    app.mount('#app')
</script>
</html>